package com.ruoyi.eqmonitor.service.impl;

import com.ruoyi.eqmonitor.domain.OptDeviceAlarm;
import com.ruoyi.eqmonitor.dtos.AlarmSearchInput;
import com.ruoyi.eqmonitor.dtos.OptDeviceAlarmDto;
import com.ruoyi.eqmonitor.dtos.OptDeviceDto;
import com.ruoyi.eqmonitor.dtos.OptDeviceHistoryDto;
import com.ruoyi.eqmonitor.service.IOptDeviceAlarmService;
import com.ruoyi.eqmonitor.service.IOptDeviceMonitorDataService;
import com.ruoyi.eqmonitor.service.IOptDeviceService;
import com.ruoyi.eqmonitor.service.OptDeviceRealTimeDataService;
import com.ruoyi.eqmonitor.utils.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OptDeviceRealTimeDataServiceIpm implements OptDeviceRealTimeDataService {

    @Resource
    private IOptDeviceMonitorDataService iOptDeviceMonitorDataService;

    @Resource
    private IOptDeviceService iOptDeviceService;

    @Resource
    private IOptDeviceAlarmService optDeviceAlarmService;

    @Override
    public List<Map<String, Object>> selectAnDanLiuLiang(){
        // 查询氨氮一体机设备设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        //optDeviceDto.setDeviceType(Constant.EquipmentType.SR500_L_Y0_LDN.getCode());
        List<OptDeviceDto> deviceDtos = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        List<String> monitorCode = new ArrayList<>();
        monitorCode.add(Constant.MonitorInfo.M_0x0528.getCode());
        monitorCode.add(Constant.MonitorInfo.M_0x0592.getCode());
        monitorCode.add(Constant.MonitorInfo.M_0x05D1.getCode());
        return queryReal(deviceDtos, monitorCode);
    }

    @Override
    public List<Map<String, Object>> selectRiverway() {
        // 查询杆式河道监测站设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        optDeviceDto.setDeviceType(Constant.EquipmentType.SE200_L_Y0.getCode());
        List<OptDeviceDto> deviceDtos = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        List<String> monitorCode = new ArrayList<>();
        monitorCode.add(Constant.MonitorInfo.M_0x05D1.getCode());
        monitorCode.add(Constant.MonitorInfo.M_0x0592.getCode());
        monitorCode.add(Constant.MonitorInfo.M_0x0566.getCode());
        return queryReal(deviceDtos, monitorCode);
    }

    @Override
    public List<Map<String, Object>> selectRiverwayY() {
        // 查询一杆式河道监测站设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        optDeviceDto.setDeviceType(Constant.EquipmentType.SE200_L_Y0.getCode());
        List<OptDeviceDto> deviceDtos = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        List<String> monitorCode = new ArrayList<>();
        monitorCode.add(Constant.MonitorInfo.M_0x0566.getCode());
        return queryReal(deviceDtos, monitorCode);
    }

    @Override
    public List<Map<String, Object>> selectWaterQuality() {
        // 查询原位水质监测仪设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        //optDeviceDto.setDeviceType(Constant.EquipmentType.SR500_L_Y0_SNY.getCode());
        List<OptDeviceDto> deviceDtos = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        List<String> monitorCode = new ArrayList<>();
        monitorCode.add(Constant.MonitorInfo.M_0x05D1.getCode());
        monitorCode.add(Constant.MonitorInfo.M_0x0592.getCode());
        return queryReal(deviceDtos, monitorCode);
    }

    @Override
    public List<Map<String, Object>> selectManhole() {
        // 查询窨井液位监测仪设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        optDeviceDto.setDeviceType(Constant.EquipmentType.SY800_L_Y0.getCode());
        List<OptDeviceDto> deviceDtos = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        List<String> monitorCode = new ArrayList<>();
        monitorCode.add(Constant.MonitorInfo.M_0x05D1.getCode());
        return queryReal(deviceDtos, monitorCode);
    }

    /**
     * 查询实时数据
     * @param deviceDtos 设备编码
     * @param monitorCodes 监测数据类型编码
     */
    private List<Map<String, Object>> queryReal(List<OptDeviceDto> deviceDtos,List<String> monitorCodes){
        OptDeviceHistoryDto historyDto = new OptDeviceHistoryDto();
        //起始时间为当天0点时间，结束时间为当前时间
        LocalDate today = LocalDate.now();
        LocalDateTime todayStart = today.atStartOfDay();
        Date startTime = Date.from(todayStart.atZone(ZoneId.systemDefault()).toInstant());
        historyDto.setStartTime(startTime);
        Date endTime = new Date();
        historyDto.setEndTime(endTime);

        List<String> deviceCodeList = deviceDtos.stream()
                .map(OptDeviceDto::getDeviceCode) // 获取设备编码
                .distinct() // 去重
                .collect(Collectors.toList());
        historyDto.setDeviceCodes(deviceCodeList);

        historyDto.setDownsample("1h");
        historyDto.setAggregator("avg");

        //查询报警信息，判断是否报警
        AlarmSearchInput alarmSearchInput = new AlarmSearchInput();
        alarmSearchInput.setDeviceCodes(deviceCodeList);
        alarmSearchInput.setAlarmCodes(monitorCodes);
        alarmSearchInput.setStartTime(startTime);
        alarmSearchInput.setEndTime(endTime);
        List<OptDeviceAlarmDto> optDeviceAlarms = optDeviceAlarmService.listAndDispatch(alarmSearchInput);

        List<Map<String, Object>> list = new ArrayList<>();
        monitorCodes.forEach(code ->{
            historyDto.setMonitorCode(code);
            Map<String, List<Map<String, Object>>> history = iOptDeviceMonitorDataService.getDownsampleHistory(historyDto);
            List<Map<String, Object>> maps = history.entrySet().stream()
                    .filter(e -> e.getValue().size() > 0)
                    .map(e -> {
                        //时间、值
                        List<Map<String, Object>> list1 = e.getValue();
                        Map<String, Object> map = new HashMap<>();
                        List<String> dateTimeList = list1.stream()
                                .map(m -> m.get("date").toString() + " " + m.get("time").toString())
                                .collect(Collectors.toList());
                        List<String> valueList = list1.stream()
                                .map(m -> m.get("value").toString())
                                .collect(Collectors.toList());
                        map.put("time", dateTimeList);
                        map.put("value", valueList);

                        //设备信息
                        Optional<OptDeviceDto> optionalDevice = deviceDtos.stream()
                                .filter(dto -> dto.getDeviceCode().equals(e.getKey())).findFirst();
                        optionalDevice.ifPresent(device -> {
                            map.put("device", device);

                            //tags信息
                            Map<String, String> tagsMap = new HashMap<>();
                            tagsMap.put("snCode", device.getDeviceCode());
                            tagsMap.put("dataUnit", list1.get(0).get("unit").toString());
                            tagsMap.put("equipmentType", Constant.EquipmentType.getValueByCode(device.getDeviceType()));
                            tagsMap.put("equipmentTypeCode", device.getDeviceType());
                            tagsMap.put("monitorCode", code);
                            tagsMap.put("monitorName", Constant.MonitorInfo.getMonitorInfoByCode(code).getName());
                            map.put("tags", tagsMap);
                            //为保证数据格式的一致性，此处去去除
                            map.put("deviceAlarmUpperLimit", null);
                            map.put("deviceAlarmFloorLimit", null);

                            //报警信息---isAlarm（是否报警），alarmValue（报警值）
                            List<OptDeviceAlarm> collect = optDeviceAlarms.stream()
                                    .filter(alarm -> alarm.getDeviceCode().equals(device.getDeviceCode())
                                            && alarm.getAlarmCode().equals(code))
                                    .collect(Collectors.toList());

                            if (collect.size() > 0) {
                                map.put("isAlarm", true);
                                map.put("alarmValue", collect.get(0).getAlarmCodeValue());
                            } else {
                                map.put("isAlarm", false);
                                map.put("alarmValue", null);
                            }
                        });

                        return map;
                    })
                    .collect(Collectors.toList());
            list.addAll(maps);
        });

        return list;
    }

}
